package com.yangm.provider;

import com.yangm.core.RpcServiceHelper;
import com.yangm.core.ServiceMeta;
import com.yangm.coder.MiniRpcDecoder;
import com.yangm.coder.MiniRpcEncoder;
import com.yangm.handler.RpcRequestHandler;
import com.yangm.provider.anotation.RpcService;
import com.yangm.registry.RegistryService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class RpcProvider implements InitializingBean, BeanPostProcessor {

    private String serviceAddress;
    private final int serverPort;
    private final RegistryService serviceRegistry;

    private final Map<String, Object> rpcServiceMap = new HashMap();


    public RpcProvider(int serverPort, RegistryService serviceRegistry) {
        this.serverPort = serverPort;
        this.serviceRegistry = serviceRegistry;
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        new Thread(() -> {
            try {
                startServer();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    /**
     * 启动服务
     */
    private void startServer() throws Exception {
        this.serviceAddress = InetAddress.getLocalHost().getHostAddress();
        log.info("startServer serviceAddress is {}", serviceAddress);
        this.serviceAddress="127.0.0.1";
        EventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try {
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new MiniRpcEncoder())
                                    .addLast(new MiniRpcDecoder())
                                    .addLast(new RpcRequestHandler(rpcServiceMap));

                        }
                    }).childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture cf = bootstrap.bind(this.serviceAddress, this.serverPort).sync();
            log.info("server addr {} stared on port{}", this.serviceAddress, this.serverPort);
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
        if (null != rpcService) {
            String serviceName = rpcService.serviceInterFace().getName();
            String serviceVersion = rpcService.serviceVersion();
            ServiceMeta serviceMeta = new ServiceMeta();
            serviceMeta.setServiceAddress(serviceAddress);
            serviceMeta.setServiceName(serviceName);
            serviceMeta.setServicePort(serverPort);
            serviceMeta.setServiceVersion(serviceVersion);

            try {
                log.info("register service {}#{}, address{}:{}", serviceName, serviceVersion, serviceAddress,serverPort);
                serviceRegistry.register(serviceMeta);
                rpcServiceMap.put(RpcServiceHelper.buildServiceKey(serviceName, serviceVersion), bean);
            } catch (Exception e) {
                log.error("failed to register service {}#{}", serviceName, serviceVersion, e);
            }
        }
        return bean;
    }
}
